package com.zecan.blog.filter;

import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zecan.blog.common.status.UserStatus;
import com.zecan.blog.common.status.code.UserCode;
import com.zecan.blog.controller.exception.UserException;
import com.zecan.blog.dao.UserMapper;
import com.zecan.blog.entity.BlogUser;
import com.zecan.blog.security.entity.AccountUser;
import com.zecan.blog.utils.*;
import com.zecan.blog.vo.user.BlogUserInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.User;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName JwtLoginFilter
 * @Description
 * @date 2022/11/1 19:18
 * @Version 1.0
 * 登录过滤器
 */
@Slf4j
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {
    private AuthenticationManager authenticationManager;


    // 获取 dao

    UserMapper userMapper = SpringUtils.getBean(UserMapper.class);
    RedisUtils redisUtils = SpringUtils.getBean(RedisUtils.class);
    JWTUtils jwtUtils = SpringUtils.getBean(JWTUtils.class);

    public JwtLoginFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
        // 设置登录路径
        super.setFilterProcessesUrl("/login");
    }

    // 登录验证
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

        try {
            //ServletOutputStream outputStream = response.getOutputStream();
            // 将前端传递过来的 表单数据转化成 实体类
            AccountUser user = new ObjectMapper().readValue(request.getInputStream(), AccountUser.class);

            // 验证验证码信息
            validate(user.getCaptchaCode(), user.getCaptchaKey());
            // 返回当前用户的信息，并进行登录验证 ==> 从 UserDetailServiceImpl中使用loadUserByUsername方法进行验证
            Authentication authenticate = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(
                            user.getUsername(), user.getPassword()));

            //
            return authenticate;

        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }


    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain
            chain, Authentication authResult) throws IOException, ServletException {
        response.setContentType("application/json;charset=UTF-8");
        ServletOutputStream outputStream = response.getOutputStream();


        // 生成jwt 并放置在响应中
        String jwt = jwtUtils.generateToken(authResult.getName());
        // key -value
        response.setHeader(jwtUtils.getHeader(), jwt);

        // 返回响应前端的JSON
        AccountUser principal = (AccountUser) authResult.getPrincipal();
        //登录成功后获取用户的头像地址, id , 用户名 , 昵称, 邮箱
        BlogUserInfoVo userInfoVo = BlogUserInfoVo.getUserInfoVo(principal);
        outputStream.write(
                JSONUtil.toJsonStr(BlogResultUtils.userStatus(userInfoVo, UserStatus.USER_LOGIN_SUCCESS)).getBytes(StandardCharsets.UTF_8)
        );
        // 关闭流
        outputStream.flush();
        outputStream.close();
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse
            response, AuthenticationException failed) throws IOException, ServletException {
        response.setContentType("application/json;charset=UTF-8");
        ServletOutputStream outputStream = response.getOutputStream();


        // 登录失败: 可能是用户名密码错误, 可能是验证码错误
        if (failed instanceof CaptchaException) {
            outputStream.write(JSONUtil.toJsonStr(BlogResultUtils.userStatus(UserStatus.USER_CAPTCHA_ERR)).getBytes(StandardCharsets.UTF_8));
        } else {
            outputStream.write(JSONUtil.toJsonStr(BlogResultUtils.userStatus(UserStatus.USER_LOGIN_ERR)).getBytes(StandardCharsets.UTF_8));
        }

        outputStream.flush();
        outputStream.close();

    }

    private void validate(String captchaCode, String captchaKey) {
        // 判断当前验证码与验证码的key值是否为不存在
        if (StringUtils.isBlank(captchaCode) || StringUtils.isBlank(captchaKey)) {
            throw new CaptchaException("验证码输入错误");
        }
        // 查询redis 如果为假则报错
        if (!captchaCode.equals(redisUtils.hashGet(BlogConstant.CAPTCHA_KEY, captchaKey))) {
            throw new CaptchaException("验证码已过期");
        }
        //删除对应的验证码 在redis中
        redisUtils.delHash(BlogConstant.CAPTCHA_KEY, captchaKey);
    }

    // 获取权限信息
    private Authentication getAuthentication(Long userId) {
        List<GrantedAuthority> authorityList = null;
        // 保存权限信息
        switch (userMapper.findUserAuthenticationInfoById(userId)) {
            case 0:
                authorityList = AuthorityUtils.createAuthorityList(BlogConstant.BLOG_NORMAL_USER);
            case 1:
                authorityList = AuthorityUtils.createAuthorityList(BlogConstant.BLOG_ADMIN);
        }
        return null;
    }
}
